#include <string.h>
#include <sys/stat.h>

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/system.h>

struct m_inode inode_table[NR_INODE]={{0, }, };

static inline void wait_on_inode(struct m_inode *inode)
{
    cli();
    while (inode->i_lock)
        sleep_on(&inode->i_wait);
    sti();
}

static void read_inode(struct m_inode *inode);

void iput(struct m_inode *inode)
{
    if (!inode)
        return;
    wait_on_inode(inode);
    if (!inode->i_count)
        panic("iput: trying to free free inode");
    if (inode->i_pipe) {
        wake_up(&inode->i_wait);
        if (--inode->i_count)
            return;
        free_page(inode->i_size);
        inode->i_count=0;
        inode->i_dirt=0;
        inode->i_pipe=0;
        return;
    }
    if (!inode->i_dev) {
        inode->i_count--;
        return;
    }
repeat:
    if (inode->i_count>1) {
        inode->i_count--;
        return;
    }
    inode->i_count--;
    return;
}

struct m_inode *get_empty_inode(void)
{
    struct m_inode *inode;
    static struct m_inode *last_inode = inode_table;
    int i;

    do {
        inode = NULL;
        for (i = NR_INODE; i; i--) {
            if (++last_inode >= inode_table + NR_INODE)
                last_inode = inode_table;
            if (!last_inode->i_count) {
                inode = last_inode;
                if (!inode->i_dirt && !inode->i_lock)
                    break;
            }
        }
        if (!inode) {
            for (i = 0; i < NR_INODE; i++)
                printk("%04x: %6d\t", inode_table[i].i_dev, 
                    inode_table[i].i_num);
            panic("No free inodes in mem");
        }
    } while (inode->i_count);
    memset(inode, 0, sizeof(*inode));
    inode->i_count = 1;
    return inode;
}

struct m_inode *iget(int dev, int nr)
{
    struct m_inode *inode, *empty;

    if (!dev)
        panic("iget with dev == 0");
    empty = get_empty_inode();
    inode = inode_table;
    while (inode < NR_INODE + inode_table) {
        if (inode->i_dev != dev || inode->i_num != nr) {
            inode++;
            continue;
        }
        if (inode->i_dev != dev || inode->i_num != nr) {
            inode = inode_table;
            continue;
        }
        inode->i_count++;
        return inode;
    }
    if (!empty)
        return (NULL);
    inode = empty;
    inode->i_dev = dev;
    inode->i_num = nr;
    read_inode(inode);
    return inode;
}

static void read_inode(struct m_inode * inode)
{
    struct super_block *sb;
    struct buffer_head *bh;
    int block;

    sb = get_super(inode->i_dev);
    block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num - 1) / INODES_PER_BLOCK;
    bh = bread(inode->i_dev, block);
    if (!bh)
        panic("unable to read i-node block");
    __asm__("cld"::);
    *(struct d_inode *)inode = ((struct d_inode *) bh->b_data)[(inode->i_num - 1) % INODES_PER_BLOCK];
    brelse(bh);
}